package com.icesoft.framework.core.util;

import lombok.extern.slf4j.Slf4j;

import java.io.Serializable;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.function.Function;


/**
 * 数字类型 帮助类
 */
@Slf4j
@SuppressWarnings("unused")
public class NumberUtil {
	/**
	 * 汉语中数字大写
	 */
	private static final String[] CN_UPPER_NUMBER = {"零", "壹", "贰", "叁", "肆",
			"伍", "陆", "柒", "捌", "玖"};
	/**
	 * 汉语中货币单位大写，这样的设计类似于占位符
	 */
	private static final String[] CN_UPPER_MONETRAY_UNIT = {"分", "角", "元",
			"拾", "佰", "仟", "万", "拾", "佰", "仟", "亿", "拾", "佰", "仟", "兆", "拾",
			"佰", "仟"};
	/**
	 * 特殊字符：整
	 */
	private static final String CN_FULL = "整";
	/**
	 * 特殊字符：负
	 */
	private static final String CN_NEGATIVE = "负";
	/**
	 * 金额的精度，默认值为2
	 */
	private static final int MONEY_PRECISION = 2;
	/**
	 * 特殊字符：零元整
	 */
	private static final String CN_ZEOR_FULL = "零元" + CN_FULL;

	private static <T, R> R valueOfDef(T t, Function<T, R> function, R def) {
		try {
			return function.apply(t);
		} catch (Exception e) {
			return def;
		}
	}

	public static Long longValueOfNil(String value) {
		return valueOfDef(value, Long::valueOf, null);
	}

	public static Long longValueOf0(String value) {
		return valueOfDef(value, Long::valueOf, 0L);
	}

	public static Long longValueOfNil(Object value) {
		return valueOfDef(value, (val) -> Long.valueOf(val.toString()), null);
	}

	public static Long longValueOf0(Object value) {
		return valueOfDef(value, (val) -> Long.valueOf(val.toString()), 0L);
	}

	public static Boolean boolValueOfFalse(Object value) {
		return valueOfDef(value, (val) -> Boolean.valueOf(val.toString()), false);
	}

	/**
	 * bool转其他类型
	 *
	 * @param value      数据
	 * @param successVal 成功类型
	 * @param falseVal   失败类型
	 */
	public static <R, T extends R, F extends R> R boolValueOfNum(Object value, T successVal, F falseVal) {
		return valueOfDef(value, (val) -> Boolean.TRUE.equals(val) ? successVal : falseVal, falseVal);
	}

	public static Integer intValueOfNil(String value) {
		return valueOfDef(value, Integer::valueOf, null);
	}

	/**
	 * <pre style="color:red">
	 * 注意value真实值大于Integer.MIN_VALUE 返回 0
	 * </pre>
	 */
	public static Integer intValueOf0(String value) {
		return intValueOf(value, 0);
	}

	public static Integer intValueOf(String value, Integer def) {
		return valueOfDef(value, Integer::valueOf, def);
	}

	public static Integer intValueOfNil(Object value) {
		return valueOfDef(value, (val) -> Integer.valueOf(val.toString()), null);
	}

	public static Integer intValueOf0(Object value) {
		return valueOfDef(value, (val) -> Integer.valueOf(val.toString()), 0);
	}

	public static Double doubleValueOf0(Object value) {
		return valueOfDef(value, (val) -> Double.valueOf(val.toString()), 0D);
	}

	public static Integer getOrDef(Integer val, Integer def) {
		return val == null ? def : val;
	}

	public static Long getOrDef(Long val, Long def) {
		return val == null ? def : val;
	}

	public static Double getOrDef(Double val, Double def) {
		return val == null ? def : val;
	}

	public static Boolean getOrDef(Boolean val, Boolean def) {
		return val == null ? def : val;
	}

	public static List<Integer> initList(int len) {
		return initList(0, len);
	}

	/**
	 * 初始化一组数据
	 *
	 * @param start 开始数据
	 * @param len   生成的数组长度
	 */
	public static List<Integer> initList(Integer start, int len) {
		return initList(start, len, t -> Integer.valueOf(String.valueOf(t)));
	}

	public static List<Long> initList(Long start, int len) {
		return initList(start, len, t -> Long.valueOf(String.valueOf(t)));
	}

	public static List<Float> initList(Float start, int len) {
		return initPointList(start, len, t -> Float.valueOf(String.valueOf(t)));
	}

	public static List<Double> initList(Double start, int len) {
		return initPointList(start, len, t -> Double.valueOf(String.valueOf(t)));
	}

	private static <T extends Serializable> List<T> initPointList(T start, int len, Function<Double, T> coverFunc) {
		List<T> list = new ArrayList<>();
		double j = Double.parseDouble(String.valueOf(start));
		for (double i = j; i < (j + len); i++) {
			list.add(coverFunc.apply(i));
		}
		return list;
	}

	private static <T extends Serializable> List<T> initList(T start, int len, Function<Long, T> coverFunc) {
		List<T> list = new ArrayList<>();
		if (start instanceof Integer || start instanceof Long) {
			long j = Long.parseLong(String.valueOf(start));
			for (long i = j; i < (j + len); i++) {
				list.add(coverFunc.apply(i));
			}
		}
		return list;
	}

	public static <T> boolean isG0(T val) {
		if (!Objects.isNull(val)) {
			if (val instanceof String) {
				if (!RegexUtil.isNumVal(String.valueOf(val))) {
					return false;
				}
			}
			//浮点数判断
			if (val instanceof Double || val instanceof Float) {
				return NumberUtil.doubleValueOf0(val) > 0D;
			}
			//整形判断
			if (val instanceof Long || val instanceof Integer) {
				return NumberUtil.longValueOf0(val) > 0L;
			}
		}
		return false;
	}

	public static <T> boolean isGe0(T val) {
		if (!Objects.isNull(val)) {
			if (val instanceof String) {
				if (!RegexUtil.isNumVal(String.valueOf(val))) {
					return false;
				}
			}
			//浮点数判断
			if (val instanceof Double || val instanceof Float) {
				return NumberUtil.doubleValueOf0(val) >= 0D;
			}
			//整形判断
			if (val instanceof Long || val instanceof Integer) {
				return NumberUtil.longValueOf0(val) >= 0L;
			}
		}
		return false;
	}

	/**
	 * 把输入的金额转换为汉语中人民币的大写
	 *
	 * @param numberOfMoney 输入的金额
	 * @return 对应的汉语大写
	 */
	public static String number2CNMontrayUnit(BigDecimal numberOfMoney) {
		StringBuffer sb = new StringBuffer();
		// -1, 0, or 1 as the value of this BigDecimal is negative, zero, or
		// positive.
		int signum = numberOfMoney.signum();
		// 零元整的情况
		if (signum == 0) {
			return CN_ZEOR_FULL;
		}
		//这里会进行金额的四舍五入
		long number = numberOfMoney.movePointRight(MONEY_PRECISION)
				.setScale(0, RoundingMode.HALF_UP).abs().longValue();
		// 得到小数点后两位值
		long scale = number % 100;
		int numUnit = 0;
		int numIndex = 0;
		boolean getZero = false;
		// 判断最后两位数，一共有四中情况：00 = 0, 01 = 1, 10, 11
		if (!(scale > 0)) {
			numIndex = 2;
			number = number / 100;
			getZero = true;
		}
		if ((scale > 0) && (!(scale % 10 > 0))) {
			numIndex = 1;
			number = number / 10;
			getZero = true;
		}
		int zeroSize = 0;
		while (true) {
			if (number <= 0) {
				break;
			}
			// 每次获取到最后一个数
			numUnit = (int) (number % 10);
			if (numUnit > 0) {
				if ((numIndex == 9) && (zeroSize >= 3)) {
					sb.insert(0, CN_UPPER_MONETRAY_UNIT[6]);
				}
				if ((numIndex == 13) && (zeroSize >= 3)) {
					sb.insert(0, CN_UPPER_MONETRAY_UNIT[10]);
				}
				sb.insert(0, CN_UPPER_MONETRAY_UNIT[numIndex]);
				sb.insert(0, CN_UPPER_NUMBER[numUnit]);
				getZero = false;
				zeroSize = 0;
			} else {
				++zeroSize;
				if (!(getZero)) {
					sb.insert(0, CN_UPPER_NUMBER[numUnit]);
				}
				if (numIndex == 2) {
					if (number > 0) {
						sb.insert(0, CN_UPPER_MONETRAY_UNIT[numIndex]);
					}
				} else if (((numIndex - 2) % 4 == 0) && (number % 1000 > 0)) {
					sb.insert(0, CN_UPPER_MONETRAY_UNIT[numIndex]);
				}
				getZero = true;
			}
			// 让number每次都去掉最后一个数
			number = number / 10;
			++numIndex;
		}
		// 如果signum == -1，则说明输入的数字为负数，就在最前面追加特殊字符：负
		if (signum == -1) {
			sb.insert(0, CN_NEGATIVE);
		}
		// 输入的数字小数点后两位为"00"的情况，则要在最后追加特殊字符：整
		if (!(scale > 0)) {
			sb.append(CN_FULL);
		}
		return sb.toString();
	}


	public static int chineseNumber2Int(String chineseNumber) {
		int result = 0;
		int temp = 1;//存放一个单位的数字如：十万
		int count = 0;//判断是否有chArr
		char[] cnArr = new char[]{'一', '二', '三', '四', '五', '六', '七', '八', '九'};
		char[] chArr = new char[]{'十', '百', '千', '万', '亿'};
		for (int i = 0; i < chineseNumber.length(); i++) {
			boolean b = true;//判断是否是chArr
			char c = chineseNumber.charAt(i);
			for (int j = 0; j < cnArr.length; j++) {//非单位，即数字
				if (c == cnArr[j]) {
					if (0 != count) {//添加下一个单位之前，先把上一个单位值添加到结果中
						result += temp;
						temp = 1;
						count = 0;
					}
					// 下标+1，就是对应的值
					temp = j + 1;
					b = false;
					break;
				}
			}
			if (b) {//单位{'十','百','千','万','亿'}
				for (int j = 0; j < chArr.length; j++) {
					if (c == chArr[j]) {
						switch (j) {
							case 0:
								temp *= 10;
								break;
							case 1:
								temp *= 100;
								break;
							case 2:
								temp *= 1000;
								break;
							case 3:
								temp *= 10000;
								break;
							case 4:
								temp *= 100000000;
								break;
							default:
								break;
						}
						count++;
					}
				}
			}
			if (i == chineseNumber.length() - 1) {//遍历到最后一个字符
				result += temp;
			}
		}
		return result;
	}


	public static String ToCH(int intInput) {
		String si = String.valueOf(intInput);
		String sd = "";
		if (si.length() == 1) // 個
		{
			sd += GetCH(intInput);
			return sd;
		} else if (si.length() == 2)// 十
		{
			if (si.charAt(0) == '1')
				sd += "十";
			else
				sd += (GetCH(intInput / 10) + "十");
			sd += ToCH(intInput % 10);
		} else if (si.length() == 3)// 百
		{
			sd += (GetCH(intInput / 100) + "百");
			if (String.valueOf(intInput % 100).length() < 2)
				sd += "零";
			sd += ToCH(intInput % 100);
		} else if (si.length() == 4)// 千
		{
			sd += (GetCH(intInput / 1000) + "千");
			if (String.valueOf(intInput % 1000).length() < 3)
				sd += "零";
			sd += ToCH(intInput % 1000);
		} else if (si.length() == 5)// 萬
		{
			sd += (GetCH(intInput / 10000) + "萬");
			if (String.valueOf(intInput % 10000).length() < 4)
				sd += "零";
			sd += ToCH(intInput % 10000);
		}

		return sd;
	}

	private static String GetCH(int input) {
		String[] cnArr = new String[]{"一", "二", "三", "四", "五", "六", "七", "八", "九"};
		return cnArr[input];
	}


	public static void main(String[] args) {
		double money = 2020104.01;
		BigDecimal numberOfMoney = new BigDecimal(money);
		String s = number2CNMontrayUnit(numberOfMoney);
		System.out.println("你输入的金额为：【" + money + "】   #--# [" + s + "]");


		System.out.println(ToCH(11));
	}


}


